/**
 * Copyright (c) 2009 Netflix-Flex_API Contributors.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of 
 * this software and associated documentation files (the "Software"), to deal in 
 * the Software without restriction, including without limitation the rights to 
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
 * of the Software, and to permit persons to whom the Software is furnished to do 
 * so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all 
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 * */
package com.netflix.webapis.catalog
{
	import com.netflix.webapis.ParamsBase;
	import com.netflix.webapis.ServiceBase;
	import com.netflix.webapis.ServiceFault;
	import com.netflix.webapis.catalog.model.CatalogItemModelBase;
	import com.netflix.webapis.catalog.model.DiscCatalogItemModel;
	import com.netflix.webapis.catalog.model.MovieCatalogItemModel;
	import com.netflix.webapis.catalog.model.ProgramCatalogItemModel;
	import com.netflix.webapis.catalog.model.SeasonCatalogItemModel;
	import com.netflix.webapis.catalog.model.SeriesCatalogItemModel;
	import com.netflix.webapis.catalog.params.CatalogParams;
	import com.netflix.webapis.catalog.vo.AutoCompleteItemVO;
	import com.netflix.webapis.catalog.vo.CatalogTitleVO;
	import com.netflix.webapis.events.NetflixFaultEvent;
	import com.netflix.webapis.events.NetflixResultEvent;
	import com.netflix.webapis.xml.NetflixXMLUtil;
	
	import mx.rpc.AsyncToken;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;
	
	import org.iotashan.oauth.OAuthRequest;

	/**
	 * Catalog Services under the <i>Titles</i> category. This is the main catagory for retrieving movie and series information.
	 * @author jonbcampos
	 * 
	 */	
	public class TitlesService extends ServiceBase
	{
		public function TitlesService()
		{
			super();
		}
		
		//----------------------------
		//
		// Public Static Consts
		//
		//----------------------------
		public static const AUTOCOMPLETE_SERVICE:String = "autoComplete";
		public static const CATALOG_SERVICE:String = "catalog";
		public static const MOVIE_SERVICE:String = "movie";
		public static const SERIES_SERVICE:String = "series";
		public static const SEASON_SERVICE:String = "season";
		public static const PROGRAM_SERVICE:String = "program";
		public static const DISC_SERVICE:String = "disc";
		
		
		//----------------------------
		//
		// Protected Static Consts
		//
		//----------------------------
		protected static const TITLES_AUTOCOMPLETE_URL:String = NETFLIX_BASE_URL+"catalog/titles/autocomplete";
		protected static const TITLES_CATALOG_URL:String = NETFLIX_BASE_URL+"catalog/titles";
		protected static const TITLES_MOVIE_URL:String = NETFLIX_BASE_URL+"catalog/titles/movies";
		protected static const TITLES_SERIES_URL:String = NETFLIX_BASE_URL+"catalog/titles/series";
		protected static const TITLES_SEASON_PART:String = "seasons";
		protected static const TITLES_PROGRAM_URL:String = NETFLIX_BASE_URL+"catalog/titles/program";
		protected static const TITLES_DISC_URL:String = NETFLIX_BASE_URL+"catalog/titles/discs";
		
		//----------------------------
		//
		// Public Variables
		//
		//----------------------------
		[Bindable]
		[Inspectable(enumeration="autoComplete,catalog,movie,series,season,program,disc",defaultValue="movie")]
		public var type:String = MOVIE_SERVICE;
		
		//----------------------------
		//
		// Private Variables
		//
		//----------------------------
		private var _service:HTTPService;
		
		
		//----------------------------
		//
		// Public Methods
		//
		//----------------------------
		/**
		 * @inherit
		 */		
		override public function send(parameters:ParamsBase = null):void
		{
			super.send(parameters);

			switch(type)
			{
				case AUTOCOMPLETE_SERVICE:
					autoCompleteService( request );
				break;
				case MOVIE_SERVICE:
					moviesService( request );
				break;
				case SERIES_SERVICE:
					seriesService( request );
				break;
				case SEASON_SERVICE:
					seasonService( request );
				break;
				case PROGRAM_SERVICE:
					programService( request );
				break;
				case DISC_SERVICE:
					discService( request );
				break;
				default:
					catalogService( request );
				break;
			}
		}
		
		/**
		 * Calls for the autoComplete Service. 
		 * @param params
		 * 
		 */		
		public function autoCompleteService(params:ParamsBase=null):void
		{
			handleServiceLoading(TITLES_AUTOCOMPLETE_URL,NetflixResultEvent.AUTOCOMPLETE_SERVICE_RESULT,determineParams(params,AUTOCOMPLETE_SERVICE));
		}
		
		/**
		 * Calls for the catalog Service. 
		 * @param params
		 * 
		 */		
		public function catalogService(params:ParamsBase=null):void
		{
			handleServiceLoading(TITLES_CATALOG_URL,NetflixResultEvent.CATALOG_SERVICE_RESULT,determineParams(params,CATALOG_SERVICE));
		}
		
		/**
		 * Calls for the movies Service. 
		 * @param params
		 * 
		 */		
		public function moviesService(params:ParamsBase=null):void
		{
			handleServiceLoading(TITLES_MOVIE_URL,NetflixResultEvent.MOVIE_SERVICE_RESULT,determineParams(params,MOVIE_SERVICE));
		}
		
		/**
		 * Calls for the seriesService Service. 
		 * @param params
		 * 
		 */	
		public function seriesService(params:ParamsBase=null):void
		{
			handleServiceLoading(TITLES_SERIES_URL,NetflixResultEvent.SERIES_SERVICE_RESULT,determineParams(params,SERIES_SERVICE));
		}
		
		/**
		 * Calls for the seasonService Service. 
		 * @param params
		 * 
		 */	
		public function seasonService(params:ParamsBase=null):void
		{
			handleServiceLoading(TITLES_SERIES_URL,NetflixResultEvent.SEASON_SERVICE_RESULT,determineParams(params,SEASON_SERVICE));
		}
		
		/**
		 * Calls for the programService Service. 
		 * @param params
		 * 
		 */	
		public function programService(params:ParamsBase=null):void
		{
			handleServiceLoading(TITLES_PROGRAM_URL,NetflixResultEvent.PROGRAM_SERVICE_RESULT,determineParams(params,PROGRAM_SERVICE));
		}
		
		/**
		 * Calls for the discService Service. 
		 * @param params
		 * 
		 */	
		public function discService(params:ParamsBase=null):void
		{
			handleServiceLoading(TITLES_DISC_URL,NetflixResultEvent.DISC_SERVICE_RESULT,determineParams(params,DISC_SERVICE));
		}
		
		/**
		 * @inherit
		 */	
		override protected function handleServiceLoading(methodString:String, dispatchType:String, params:ParamsBase = null):void
		{
			super.handleServiceLoading(methodString,dispatchType, params);
			
			var sendQuery:String = methodString;
			var typeQuery:String;
			
			if(params != null)
			{
				switch(dispatchType)
				{
					case NetflixResultEvent.AUTOCOMPLETE_SERVICE_RESULT:
						// do no adjustment
					break;
					case NetflixResultEvent.CATALOG_SERVICE_RESULT:
						// do no adjustment
					break;
					case NetflixResultEvent.MOVIE_SERVICE_RESULT:
						sendQuery += "/"+CatalogParams(params).movieID;
						if(CatalogParams(params).retrieveExpansionOnly && CatalogParams(params).expand)
							sendQuery += "/"+CatalogParams(params).expand;
					break;
					case NetflixResultEvent.SERIES_SERVICE_RESULT:
						sendQuery += "/"+CatalogParams(params).seriesID;
					break;
					case NetflixResultEvent.DISC_SERVICE_RESULT:
						sendQuery += "/"+CatalogParams(params).discID;
					break;
					case NetflixResultEvent.SEASON_SERVICE_RESULT:
						sendQuery += "/"+CatalogParams(params).seriesID+"/"+TITLES_SEASON_PART+"/"+CatalogParams(params).seasonID;
						if(CatalogParams(params).retrieveExpansionOnly && CatalogParams(params).expand)
							sendQuery += "/"+CatalogParams(params).expand;
					break;
					case NetflixResultEvent.PROGRAM_SERVICE_RESULT:
						sendQuery += "/"+CatalogParams(params).episodeID;
					break;
				}
			}
			_service = new HTTPService();
			var tokenRequest:OAuthRequest = new OAuthRequest("GET",sendQuery,params.toObject(),consumer,accessToken);
			_service.url = tokenRequest.buildRequest(SIG_METHOD);
			trace(tokenRequest.buildRequest(SIG_METHOD));
			_service.method = "GET";
			_service.resultFormat = "e4x";
			_service.addEventListener(ResultEvent.RESULT,onCompleteHandler);
			_service.addEventListener(FaultEvent.FAULT,onFault);
			_service.send();
		
			function onCompleteHandler(event:ResultEvent):void
			{
				_service.removeEventListener(ResultEvent.RESULT,onCompleteHandler);
				_service.removeEventListener(FaultEvent.FAULT,onFault);
				var queryXML:XML = event.result as XML;
				
				if(queryXML.Error == undefined)
					formatAndDispatch(queryXML, dispatchType);
				else
					dispatchFault(new ServiceFault(NetflixFaultEvent.API_RESPONSE, queryXML.Error, queryXML.Error.Message));
			}
			
			function onFault(event:FaultEvent):void
			{
				_service.removeEventListener(ResultEvent.RESULT,onCompleteHandler);
				_service.removeEventListener(FaultEvent.FAULT,onFault);
				dispatchFault(new ServiceFault(event.fault.faultCode,event.fault.faultString,event.fault.faultDetail));
			}
		}
		
		/**
		 * @inherit
		 */		
		override protected function formatAndDispatch(returnedXML:XML, dispatchType:String):void
		{
			super.formatAndDispatch(returnedXML,dispatchType);
			
			var resultsArray:Array = new Array();
			var resultNode:XML;
			
			//loop through child nodes and build value objects based on catagory type
			switch(dispatchType)
			{
				case NetflixResultEvent.AUTOCOMPLETE_SERVICE_RESULT:
					for each(resultNode in returnedXML..autocomplete_item)
					{
						var autoCompleteVo:AutoCompleteItemVO = new AutoCompleteItemVO();
						autoCompleteVo.title = resultNode.title.@short;
						resultsArray.push(autoCompleteVo);
					}
				break;
				case NetflixResultEvent.CATALOG_SERVICE_RESULT:
					for each (resultNode in returnedXML..catalog_title) {
						var catItemVO:CatalogTitleVO = NetflixXMLUtil.handleCatalogTitleNode(resultNode, new CatalogTitleVO);
						resultsArray.push(catItemVO);
					}
				break;
				case NetflixResultEvent.MOVIE_SERVICE_RESULT:
					if(request && CatalogParams(request).retrieveExpansionOnly && CatalogParams(request).expand){
						resultsArray = handleExpansionOptions(request,returnedXML);
					} else {
						var movieModel:MovieCatalogItemModel = NetflixXMLUtil.handleXMLToCatalogItemModel(returnedXML, new MovieCatalogItemModel()) as MovieCatalogItemModel;
						resultsArray.push(movieModel);
					}
				break;
				case NetflixResultEvent.SEASON_SERVICE_RESULT:
					if(request && CatalogParams(request).retrieveExpansionOnly && CatalogParams(request).expand){
						resultsArray = handleExpansionOptions(request,returnedXML);
					} else {
						var seasonModel:SeasonCatalogItemModel = NetflixXMLUtil.handleXMLToCatalogItemModel(returnedXML, new SeasonCatalogItemModel()) as SeasonCatalogItemModel;
						resultsArray.push(seasonModel);
					}
				break;
				case NetflixResultEvent.SERIES_SERVICE_RESULT:
					if(request && CatalogParams(request).retrieveExpansionOnly && CatalogParams(request).expand){
						resultsArray = handleExpansionOptions(request,returnedXML);
					} else {
						var seriesModel:SeriesCatalogItemModel = NetflixXMLUtil.handleXMLToCatalogItemModel(returnedXML, new SeriesCatalogItemModel()) as SeriesCatalogItemModel;
						resultsArray.push(seriesModel);
					}
				break;
				case NetflixResultEvent.PROGRAM_SERVICE_RESULT:
					if(request && CatalogParams(request).retrieveExpansionOnly && CatalogParams(request).expand){
						resultsArray = handleExpansionOptions(request,returnedXML);
					} else {
						var programModel:ProgramCatalogItemModel = NetflixXMLUtil.handleXMLToCatalogItemModel(returnedXML, new ProgramCatalogItemModel()) as ProgramCatalogItemModel;
						resultsArray.push(programModel);
					}
				break;
				case NetflixResultEvent.DISC_SERVICE_RESULT:
					if(request && CatalogParams(request).retrieveExpansionOnly && CatalogParams(request).expand){
						resultsArray = handleExpansionOptions(request,returnedXML);
					} else {
						var discModel:DiscCatalogItemModel = NetflixXMLUtil.handleXMLToCatalogItemModel(returnedXML, new DiscCatalogItemModel()) as DiscCatalogItemModel;
						resultsArray.push(discModel);
					}
				break;
			}
			if(resultsArray==null || resultsArray.length==0){
				dispatchFault(new ServiceFault(NetflixFaultEvent.API_RESPONSE, "Parameter Errors", "No valid 'expand' option was selected."));
				return;
			}
			dispatchResult(resultsArray,dispatchType);
		}
		
		private function handleExpansionOptions(request:ParamsBase,returnedXML:XML):Array
		{
			var resultsArray:Array = new Array();
			var resultNode:XML;
			switch(CatalogParams(request).expand){
				case CatalogItemModelBase.EXPAND_SYNOPSIS:
					resultsArray.push(returnedXML.toString());
				break;
				case CatalogItemModelBase.EXPAND_FORMATS:
					for each(resultNode in returnedXML..availability) {
						resultsArray.push(NetflixXMLUtil.handleFormatAvailability(resultNode));
					}
				break;
				case CatalogItemModelBase.EXPAND_SCREEN_FORMATS:
					for each(resultNode in returnedXML..screen_format){
						resultsArray.push(NetflixXMLUtil.handleScreenFormat(resultNode));
					}
				break;
				case CatalogItemModelBase.EXPAND_CAST:
				case CatalogItemModelBase.EXPAND_DIRECTORS:
					for each(resultNode in returnedXML..person) {
						resultsArray.push(NetflixXMLUtil.handlePerson(resultNode));
					}
				break;
				case CatalogItemModelBase.EXPAND_LANGUAGES_AND_AUDIO:
					for each(resultNode in returnedXML..language_audio_format){
						resultsArray.push(NetflixXMLUtil.handleLanguageAudioFormat(resultNode))
					}
				break;
				case CatalogItemModelBase.EXPAND_SEASONS:
				case CatalogItemModelBase.EXPAND_EPISODES:
				case CatalogItemModelBase.EXPAND_DISCS:
					//TODO Adjust this to be models
					for each (resultNode in returnedXML..catalog_title) {
						var catItemVO:CatalogTitleVO = NetflixXMLUtil.handleCatalogTitleNode(resultNode, new CatalogTitleVO);
						resultsArray.push(catItemVO);
					}
				break;
				case CatalogItemModelBase.EXPAND_SIMILARS:
					for each(resultNode in returnedXML..similars_item) {
						resultsArray.push(NetflixXMLUtil.handleXMLToCatalogItemModel(resultNode, new CatalogItemModelBase()));
					}
				break;
				case CatalogItemModelBase.EXPAND_FILMOGRAPHY:
				break;
				case CatalogItemModelBase.EXPAND_BONUS_MATERIALS:
					for each(resultNode in returnedXML..link){
						resultsArray.push(NetflixXMLUtil.handleLink(resultNode));
					}
				break;
				case CatalogItemModelBase.EXPAND_AWARDS:
					for each(resultNode in returnedXML..award_nominee){
						resultsArray.push(NetflixXMLUtil.handleAwards(resultNode));
					}
				break;
				default:
					resultsArray new Array();
				break;
			}
			return resultsArray;
		}
		
	}
}